<body></body>
<script>
  let activeEffect = null //用于收集当前激活的副作用
  let bucket = new WeakMap() //存储副作用

  const data = {
    ok: true,
    text: 'hello wrold'
  }
  // get读取值，并绑定副作用，
  // set修改值，并调用副作用函数（包含清理副作用）
  // set移除副作用，get添加副作用，是一个闭环的过程
  const obj = new Proxy(data, {
    get(target, key) {
      track(target, key)
      return target[key]
    },
    set(target, key, newVal) {
      target[key] = newVal
      trigger(target, key)
    }
  })

  function track(target, key) {
    if (!activeEffect) return target[key] //没有通过effect调用只是数据的读取，不产生副作用，也就触发视图的更新
    let depMap = bucket.get(target) //先找到这个对象 >然后找到 对象下对应的key所绑定读取到副作用，就是个Set集合
    if (!depMap) {
      bucket.set(target, (depMap = new Map())) //没有的话就建一个 属于该对象的 map
    }
    let deps = depMap.get(key) //找到了 当前属性 key的绑定副作用
    if (!deps) {
      depMap.set(key, (deps = new Set())) //没有也是建一个属于改 key的Set副作用集合
    }
    deps.add(activeEffect) //往这个属性加上副作用
    activeEffect.deps.push(deps)
  }

  function trigger(target, key) {
    let depMap = bucket.get(target)
    if (!depMap) return
    let effects = depMap.get(key)
    const effectsToRun = new Set(effects)
    effectsToRun && effectsToRun.forEach(effectFn => effectFn())
  }

  // effect函数是一个闭包，每一个effect都是封闭，先cleanup清理副作用，
  function effect(fn) {
    function effectFn() {
      cleanup(effectFn)
      activeEffect = effectFn //存一个引用
      fn() //fn 只是一个你定义的普通函数，它本身并不包含依赖收集的机制。
    }
    effectFn.deps = []
    effectFn()
  }
  function cleanup(effectFn) {
    console.log(effectFn, effectFn.deps)

    for (let deps of effectFn.deps) {
      deps.delete(effectFn)
    }
    effectFn.deps.length = 0
  }
  effect(() => {
    console.log('更新页面测试', obj.ok, obj.text)
    // document.body.innerText = obj.ok ? obj.text : 'not' //这是一个短路的运算，ok是false，不会触发text的get
  })
  setTimeout(() => (obj.ok = true), 1000) //触发第二次
  // setTimeout(() => (obj.ok = true), 1000) //触发第二次
  // setTimeout(() => (obj.ok = true), 1000) //触发第二次
  // setTimeout(() => (obj.ok = false), 1000) //触发第二次

  // setTimeout(() => (obj.text = '1'), 1300)
  // setTimeout(() => (obj.text = '2'), 1650)
  // setTimeout(() => (obj.text = '3'), 2000)

  // setTimeout(() => (obj.ok = true), 3000) // 触发第三次，
  // setTimeout(() => (obj.text = '4'), 4000) // 触发第四次 副作用
  // 打印 4次，页面 not、3、4
</script>
